package workflow

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"shqsoft.cn/pas3ui/base"
	"shqsoft.cn/pas3ui/popedom"
	"shqsoft.cn/pas3ui/workflow/entity"

	"gorm.io/gorm"
)

//RunInfo 流程运行信息
type RunInfo struct {
	RunID    int        `json:"runID"`
	WFID     int        `json:"wfid"`
	CurrStep int        `json:"currStep"`
	State    string     `json:"state"`
	StepList []StepItem `json:"stepList"`
}

//StepItem 流程运行过程项
type StepItem struct {
	ID        int        `json:"id"`
	Name      string     `json:"name"`
	State     string     `json:"state"`
	CanBack   bool       `json:"canBack"`
	Recall    bool       `json:"recall"`
	Executors []Executor `json:"executors"`
	Appointer []string   `json:"appointer"`
	MustDesc  bool       `json:"mustDesc"`
}

//Executor 执行人
type Executor struct {
	ID         int    `json:"id"`
	Name       string `json:"name"`
	Department string `json:"department"`
	Desc       string `json:"desc"`
}

//GetRecorder 获取的备案人
func GetRecorder(wfid int) ([]string, error) {
	wf := GetWorkFlow(wfid)
	if wf == nil {
		return nil, fmt.Errorf("流程ID:%d在配置中不存在！", wfid)
	}
	if wf.Recorder == "" {
		return nil, nil
	}
	roleID, err := strconv.Atoi(wf.Recorder)
	if err != nil {
		return nil, fmt.Errorf("流程%d配置错误，流程的manager应该配置为角色ID！", wfid)
	}
	userIds, err := popedom.GetRoleUsersFormRedis(roleID)
	if err != nil {
		return nil, err
	}
	return userIds, nil
}

//GetAppointer 获取流程节点的设置人
func GetAppointer(wfid int) (map[int][]string, error) {
	wf := GetWorkFlow(wfid)
	if wf == nil {
		return nil, fmt.Errorf("流程ID:%d在配置中不存在！", wfid)
	}
	aper := make(map[int][]string, 5)
	for _, act := range wf.Actions {
		if act.Appointer == "" {
			continue
		}
		roleID, err := strconv.Atoi(act.Appointer)
		if err != nil {
			return nil, fmt.Errorf("流程%d配置错误，节点%d中的appointer应该配置为角色ID！", wfid, act.Step)
		}
		if act.Appointer != "" {
			userIds, err := popedom.GetRoleUsersFormRedis(roleID)
			if err != nil {
				return nil, err
			}
			aper[act.Step] = userIds
		}
	}
	return aper, nil
}

//GetRunInfo 获取流程运行信息
func GetRunInfo(daoName string, runID int) (runInfo *RunInfo, err error) {
	if strings.Contains(daoName, "His") {
		run := entity.FlowRunHis{}
		tx := base.MYDB.Begin()
		if err = tx.First(&run, runID).Error; err != nil {
			tx.Rollback()
			return
		}
		wf := GetWorkFlow(run.WFID)
		if wf == nil {
			tx.Rollback()
			return nil, fmt.Errorf("流程ID:%d在配置中不存在！", run.WFID)
		}
		alen := len(wf.Actions)
		list := make([]StepItem, alen)
		cache := base.GetEmployeeCache()
		for i, act := range wf.Actions {
			si := StepItem{
				ID:       act.Step,
				Name:     act.Desc,
				State:    "0",
				CanBack:  act.CanBack,
				Recall:   act.Recall,
				MustDesc: act.MustDesc,
			}
			if run.Step == 0 || run.Step > act.Step || run.State == "1" {
				si.State = "2"
			} else if run.Step == act.Step && run.State == "0" {
				si.State = "1"
			}
			userids, _, descs, err := getExecutorHis(tx, runID, act.Step, true)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
			executors := make([]Executor, len(userids))
			for j, uid := range userids {
				uName, _ := cache.GetNameByID(uid)
				oName, _ := cache.GetDeptNameByID(uid)
				executors[j] = Executor{
					ID:         uid,
					Name:       uName,
					Department: oName,
					Desc:       descs[j],
				}
			}
			si.Executors = executors
			if act.Appointer != "" {
				roleID, err := strconv.Atoi(act.Appointer)
				if err != nil {
					tx.Rollback()
					return nil, fmt.Errorf("流程%d配置错误，节点%d中的appointer应该配置为角色ID！", run.WFID, act.Step)
				}
				si.Appointer, err = popedom.GetRoleUsersFormRedis(roleID)
				if err != nil {
					tx.Rollback()
					return nil, err
				}
			}
			list[i] = si
		}
		runInfo = &RunInfo{
			RunID:    runID,
			WFID:     wf.ID,
			CurrStep: run.Step,
			State:    run.State,
			StepList: list,
		}
		if runInfo.CurrStep == 0 {
			runInfo.CurrStep = len(list)
		}
		tx.Commit()
	} else {
		run := entity.FlowRun{}
		tx := base.MYDB.Begin()
		if err = tx.First(&run, runID).Error; err != nil {
			tx.Rollback()
			return
		}
		wf := GetWorkFlow(run.WFID)
		if wf == nil {
			tx.Rollback()
			return nil, fmt.Errorf("流程ID:%d在配置中不存在！", run.WFID)
		}
		alen := len(wf.Actions)
		list := make([]StepItem, alen)
		cache := base.GetEmployeeCache()
		for i, act := range wf.Actions {
			si := StepItem{
				ID:       act.Step,
				Name:     act.Desc,
				State:    "0",
				CanBack:  act.CanBack,
				Recall:   act.Recall,
				MustDesc: act.MustDesc,
			}
			if run.Step == 0 || run.Step > act.Step || run.State == "1" {
				si.State = "2"
			} else if run.Step == act.Step && run.State == "0" {
				si.State = "1"
			}
			userids, _, descs, err := getExecutor(tx, runID, act.Step, true)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
			executors := make([]Executor, len(userids))
			for j, uid := range userids {
				uName, _ := cache.GetNameByID(uid)
				oName, _ := cache.GetDeptNameByID(uid)
				executors[j] = Executor{
					ID:         uid,
					Name:       uName,
					Department: oName,
					Desc:       descs[j],
				}
			}
			si.Executors = executors
			if act.Appointer != "" {
				roleID, err := strconv.Atoi(act.Appointer)
				if err != nil {
					tx.Rollback()
					return nil, fmt.Errorf("流程%d配置错误，节点%d中的appointer应该配置为角色ID！", run.WFID, act.Step)
				}
				si.Appointer, err = popedom.GetRoleUsersFormRedis(roleID)
				if err != nil {
					tx.Rollback()
					return nil, err
				}
			}
			list[i] = si
		}
		runInfo = &RunInfo{
			RunID:    runID,
			WFID:     wf.ID,
			CurrStep: run.Step,
			State:    run.State,
			StepList: list,
		}
		if runInfo.CurrStep == 0 {
			runInfo.CurrStep = len(list)
		}
		tx.Commit()
	}
	return
}

func createStep(tx *gorm.DB, runID int, step int, userIds []int) (err error) {
	uLen := len(userIds)
	steps := make([]entity.FlowRunStep, uLen)
	for i := 0; i < uLen; i++ {
		steps[i] = entity.FlowRunStep{
			RunID:     runID,
			StepID:    step,
			EMID:      userIds[i],
			StartTime: time.Now(),
			State:     "0",
		}
		err = tx.Create(&steps[i]).Error
		if err != nil {
			return
		}
	}
	return
}

func startStep(tx *gorm.DB, runID int, step int, userID int) (err error) {
	runStep := entity.FlowRunStep{}
	tx.Where(entity.FlowRunStep{RunID: runID, StepID: step, EMID: userID}).FirstOrInit(&runStep)
	if runStep.RunID > 0 {
		runStep.StartTime = time.Now()
		runStep.State = "0"
		err = tx.Create(&runStep).Error
		if err != nil {
			return
		}
	}
	return
}

func finishStep(tx *gorm.DB, runID int, step int, userID int, toStep int, submitType, desc string, conSign bool, organFrom string) (finish bool, err error) {
	runSteps := []entity.FlowRunStep{}
	whereEnt := entity.FlowRunStep{RunID: runID, StepID: step}
	if conSign && organFrom == "" {
		whereEnt.EMID = userID
		err = tx.Where(whereEnt).Where(`"STATE" in ('0','1')`).Find(&runSteps).Error
		if err != nil || len(runSteps) == 0 {
			return false, fmt.Errorf("不是在办任务，不能提交！")
		}
		updateState(tx, userID, toStep, submitType, desc, runSteps)
	} else if conSign && organFrom != "" {
		var pdid int
		sqlStr := `SELECT "PDID" FROM "T_FLOW_RUN_STEP" A INNER JOIN "V_USER_DEPT" B ON A."EMID"=B."EMID" WHERE "RUNID"=? AND "STEPID"=? AND A."EMID"=?`
		err = tx.Raw(sqlStr, runID, step, userID).Row().Scan(&pdid)
		if err != nil {
			return
		}
		sqlStr = `SELECT A."EMID" FROM "T_FLOW_RUN_STEP" A INNER JOIN "V_USER_DEPT" B ON A."EMID"=B."EMID" WHERE "RUNID"=? AND "STEPID"=? AND "PDID"=?`
		rows, err1 := tx.Raw(sqlStr, runID, step, pdid).Rows()
		if err1 != nil {
			return
		}
		defer rows.Close()
		emids := make([]int, 0, 10)
		for rows.Next() {
			var emid int
			err = rows.Scan(&emid)
			if err != nil {
				return
			}
			emids = append(emids, emid)
		}
		err = tx.Where(`"RUNID"=? AND "EMID" IN(?) AND "STATE" IN('0','1')`, runID, emids).Find(&runSteps).Error
		if err != nil || len(runSteps) == 0 {
			return false, fmt.Errorf("不是在办任务，不能提交！")
		}
		updateState(tx, userID, toStep, submitType, desc, runSteps)
	} else {
		err = tx.Where(whereEnt).Where(`"STATE" in ('0','1')`).Find(&runSteps).Error
		if err != nil || len(runSteps) == 0 {
			return false, fmt.Errorf("不是在办任务，不能提交！")
		}
		updateState(tx, userID, toStep, submitType, desc, runSteps)
	}

	return isAllFinish(tx, runID, step, organFrom)
}

func updateState(tx *gorm.DB, userID, toStep int, submitType, desc string, runSteps []entity.FlowRunStep) (finish bool, err error) {
	now := time.Now()
	isOK := false
	for _, runStep := range runSteps {
		if userID == runStep.EMID {
			isOK = true
		}
	}
	if !isOK && submitType != "3" {
		return false, fmt.Errorf("不是在办任务，不能提交！")
	}
	for _, runStep := range runSteps {
		if runStep.RunID > 0 {
			if runStep.EMID == userID {
				runStep.State = "2"
			} else {
				runStep.State = "3"
			}
			runStep.ToStepID = toStep
			runStep.SubmitTime = &now
			runStep.SubmitType = submitType
			runStep.Description = desc
			err = tx.Save(&runStep).Error
			if err != nil {
				return
			}
		}
	}
	return
}

func isAllFinish(tx *gorm.DB, runID int, step int, organFrom string) (finish bool, err error) {
	var count int64
	if organFrom == "" {
		err = tx.Model(entity.FlowRunStep{}).Where(entity.FlowRunStep{RunID: runID, StepID: step}).Where(`"STATE" in ('0','1')`).Count(&count).Error
		if err != nil {
			return
		}
		if count == 0 {
			finish = true
		}
	} else {
		sqlStr := `SELECT COUNT(*) FROM (SELECT "PDID",MAX("STATE") FROM "T_FLOW_RUN_STEP" A INNER JOIN "V_USER_DEPT" B ON A."EMID"=B."EMID" WHERE "RUNID"=? AND "STEPID"=? GROUP BY "PDID" HAVING MAX("STATE") IN('0','1')) T`
		err = tx.Raw(sqlStr, runID, step).Row().Scan(&count)
		if err != nil {
			return
		}
		if count == 0 {
			finish = true
		}
	}
	return
}
